#include "../config.h"

#include "shellwords.h"
#include "log.h"

namespace gpyp {

//========================
// OptionParser::Option
bool OptionParser::Option::set() {
  if (type_ == VALUE_TYPE_BOOL) {
    *(bool *)value_ = true;
    return true;
  }
  return false;
}
bool OptionParser::Option::set(const Glib::ustring &value) {
  switch(type_) {
  case VALUE_TYPE_STRING:
    *((Glib::ustring *)value_) = value;
    return true;
  case VALUE_TYPE_INT:
    {
      char *end = NULL;
      long res = strtol(value.c_str(), &end, 10);
      if (end && *end == '\0') {
        *((int *)value_) = res;
        return true;
      } else {
        return false;
      }
    }
  default:
    return false;
  }
}

OptionParser::Option::Option(const Glib::ustring &flag,
                             Glib::ustring       &value) :
  type_ (VALUE_TYPE_STRING),
  flag_ (flag),
  value_(&value)
{
}
OptionParser::Option::Option(const Glib::ustring &flag,
                             int                 &value) :
  type_ (VALUE_TYPE_INT),
  flag_ (flag),
  value_(&value)
{
}
OptionParser::Option::Option(const Glib::ustring &flag,
                             bool                &value) :
  type_ (VALUE_TYPE_BOOL),
  flag_ (flag),
  value_(&value)
{
}

//========================
// OptionParser
bool OptionParser::parse(ShellLine &line) {
  for(std::list<Glib::ustring>::iterator aiter = line.args.begin();
      aiter != line.args.end();) {
    int erase = 0;
    for(boost::ptr_list<Option>::iterator oiter = options_.begin();
        oiter != options_.end(); ++oiter) {
      if (*aiter != oiter->flag_) continue;

      if (oiter->type_ == VALUE_TYPE_BOOL) {
        oiter->set();
        erase = 1;
        break;
      } else {
        if ((++aiter) == line.args.end()) {
          log::err << format(_("value of %s not specified"),
                             oiter->flag_.c_str()) << log::endl;
          return false;
        }

        if (oiter->set(*aiter)) {
          erase = 2;
          break;
        } else {
          log::err << format(_("value of %s is invalid"),
                             oiter->flag_.c_str()) << log::endl;
          return false;
        }
      }
    }
    if (erase > 0) {
      for(; erase > 1; --erase) line.args.erase(aiter--);
      if (aiter == line.args.begin()) {
        line.args.erase(aiter);
        aiter = line.args.begin();
        continue;
      } else {
        line.args.erase(aiter--);
      }
    }
    ++aiter;
  }
  return true;
}

OptionParser::OptionParser() {
}

//========================
bool parse_shell(const Glib::ustring &command, ShellLines &lines) {
  ShellLine *line = NULL;
  bool inargs = false;
  bool inword = false;
  bool escaped = false;
  char quote = 0;
  Glib::ustring word;

  lines.clear();

#define ADD_WORD() {                            \
    if (!inargs) {                              \
      line->command = word;                     \
      inargs = true;                            \
    } else {                                    \
      line->args.push_back(word);               \
    }                                           \
    word.clear();                               \
  }

  for(const char *c = command.c_str(); *c; ++c) {
    if (!line) lines.push_back(line = new ShellLine());
    char chr = *c;
    line->line += chr;
    if (inargs) {
      line->args_raw += chr;
    }
    if (escaped) {
      escaped = false;
      switch(chr) {
      case 'r': chr = '\r';   break;
      case 'n': chr = '\n';   break;
      case 't': chr = '\t';   break;
      default:
        inword = true;
        word += chr;
        continue;
      }
    }
    switch(chr) {
    case ' ':
      if (inword) {
        if (quote) {
          word += chr;
        } else {
          inword = false;
          ADD_WORD();
        }
      }
      break;
    case '\"': case '\'':
      if (quote == chr) {
        quote = 0;
      } else if (quote) {
        word += chr;
      } else {
        inword = true;
        quote = chr;
      }
      break;
    case '\\':
      if (quote == '\'') {
        word += chr;
      } else {
        escaped = true;
      }
      break;
    case ';':
    case '\n':
      if (quote) {
        word += chr;
      } else {
        line->line.erase(line->line.length() - (chr == '\n' ? 2 : 1));
        ADD_WORD();
        line = NULL;
        inargs = false;
      }
      break;
    default:
      inword = true;
      word += chr;
      break;
    }
  }
  if (inword) ADD_WORD();
  return true;
}

Glib::ustring shell_command_get_name(const Glib::ustring &command) {
  Glib::ustring res;
  gint    argc = 0;
  gchar **argv = NULL;
  if (g_shell_parse_argv(command.c_str(), &argc, &argv, NULL) && argc > 0) {
    const char *slash = strrchr(argv[0], '/');
    if (slash) {
      res = slash + 1;
    } else {
      res = argv[0];
    }
  }
  if (argv) g_strfreev(argv);
  return res;
}

}/* namespace gpyp */
